package com.tn.rtdata.examples.stockticker.client;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

import com.tn.rtdata.DataEvent;
import com.tn.rtdata.DataListener;
import com.tn.rtdata.examples.stockticker.StockTick;
import com.tn.rtdata.util.LiveMap;

/**
 * An implementation of <code>TableModel</code> that displays <code>StockTick</code>s.
 */
public class StockTickTableModel implements TableModel
{
  private static final String[] COLUMN_NAMES = new String[]{"Stock ID", "Price"};
  private static final int INDEX_ID = 0;
  private static final int INDEX_PRICE = 1;

  private List<String> stockIds;
  private Map<String, StockTick> stockTicks;
  private List<TableModelListener> tableModelListeners;

  /**
   * Creates a ne <Code>StockTickTableModel</Code> initialized with the <code>stockTicks</code>.
   */
  public StockTickTableModel(Map<String, StockTick> stockTicks)
  {
    this.stockTicks = stockTicks;
    this.stockIds = new ArrayList<String>(stockTicks.keySet());
    this.tableModelListeners = new ArrayList<TableModelListener>();

    if (stockTicks instanceof LiveMap)
    {
      ((LiveMap<String, StockTick>)stockTicks).addDataListener(new TableModelEventFactory());
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getRowCount()
  {
    return stockIds.size();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getColumnCount()
  {
    return COLUMN_NAMES.length;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getColumnName(int index)
  {
    return COLUMN_NAMES[index];
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<?> getColumnClass(int i)
  {
    return String.class;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isCellEditable(int columnIndex, int columnRow)
  {
    return false;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getValueAt(int rowIndex, int columnIndex)
  {
    StockTick stockTick = stockTicks.get(stockIds.get(rowIndex));

    switch (columnIndex)
    {
      case INDEX_ID:
        return stockTick.getStockId();

      case INDEX_PRICE:
        return stockTick.getPrice();

      default:
        return null;
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setValueAt(Object value, int columnIndex, int rowIndex)
  {
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void addTableModelListener(TableModelListener tableModelListener)
  {
    tableModelListeners.add(tableModelListener);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void removeTableModelListener(TableModelListener tableModelListener)
  {
    tableModelListeners.remove(tableModelListener);
  }

  /**
   * Fires the <code>tableModelEvent</code> to all the <code>TableModelListener</code>s.
   */
  protected void fireTableModelListener(TableModelEvent tableModelEvent)
  {
    for (TableModelListener tableModelListener : tableModelListeners)
    {
      tableModelListener.tableChanged(tableModelEvent);
    }
  }

  /**
   * An implementation of <code>DataListener</code> that fires <code>DataEvent</code>s in response
   */
  private class TableModelEventFactory implements DataListener<StockTick>
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void dataEvent(DataEvent<StockTick> dataEvent)
    {
      if (DataEvent.Type.INITIAL.equals(dataEvent.getType()) || DataEvent.Type.ADD.equals(dataEvent.getType()))
      {
        int eventIndex = stockIds.size();
        stockIds.add(dataEvent.getNewValue().getStockId());

        fireTableModelListener(
          new TableModelEvent(
            StockTickTableModel.this,
            eventIndex
          )
        );
      }
      else if (DataEvent.Type.RESET.equals(dataEvent.getType()))
      {
        int lastEventIndex = stockIds.size() - 1;
        stockIds.clear();

        fireTableModelListener(
          new TableModelEvent(
            StockTickTableModel.this,
            0,
            lastEventIndex
          )
        );
      }
      else if (DataEvent.Type.UPDATE.equals(dataEvent.getType()))
      {
        int eventIndex = stockIds.indexOf(dataEvent.getOldValue().getStockId());
        if (eventIndex >= 0)
        {
          stockIds.remove(eventIndex);
          stockIds.add(eventIndex, dataEvent.getNewValue().getStockId());
        }
        else
        {
          stockIds.add(dataEvent.getNewValue().getStockId());
          eventIndex = stockIds.size() - 1;
        }

        fireTableModelListener(
          new TableModelEvent(
            StockTickTableModel.this,
            eventIndex
          )
        );
      }
      else if (DataEvent.Type.DELETE.equals(dataEvent.getType()))
      {
        int eventIndex = stockIds.indexOf(dataEvent.getOldValue().getStockId());
        if (eventIndex >= 0)
        {
          stockIds.remove(eventIndex);
        }

        fireTableModelListener(
          new TableModelEvent(
            StockTickTableModel.this,
            eventIndex,
            eventIndex,
            TableModelEvent.ALL_COLUMNS,
            TableModelEvent.DELETE
          )
        );
      }
    }
  }
}
